import re
import openpyxl
import typing as t
import requests
import time
import click
from collections import defaultdict
from requests.cookies import cookiejar_from_dict
from bs4 import BeautifulSoup
from pathlib import Path
from pydantic import BaseModel


class Response(BaseModel):
    errcode: int
    errmsg: str | None = None
    data: dict | None = None

    def raise_for_status(self):
        if self.errcode != 200:
            raise ValueError(f"接口返回异常[{self.errcode}]: {self.errmsg}")


class Contact(BaseModel):
    name: str
    phone_number: str
    source: str | None = None


headers = {
    "Accept": "application/json",
    "Accept-Language": "en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7,en-GB;q=0.6",
    "Connection": "keep-alive",
    "Content-Type": "application/json; charset=utf-8",
    "Origin": "https://www.tankecha.com",
    "Referer": "https://www.tankecha.com/",
    "Sec-Fetch-Dest": "empty",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Site": "same-site",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0",
    "reqType": "pc",
    "sec-ch-ua": '"Not A(Brand";v="99", "Microsoft Edge";v="121", "Chromium";v="121"',
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": '"macOS"',
}

json_data = {
    "keywords": "云瑞",
    "sortField": None,
    "sortType": None,
    "pageIndex": 1,
    "pageSize": 20,
    "condition": "{}",
}


def calculate_page(total_records: int, page_size: int = 20, start_page: int = 1) -> int:
    return (
            start_page
            + (total_records // page_size)
            + (1 if total_records % page_size else 0)
            - 1
    )


class FindCompanies:
    def __init__(self, company: str, cookies: str) -> None:
        self.company = company
        self.cookies = (
            cookiejar_from_dict(
                {k: v for k, v in [cookie.split("=") for cookie in cookies.split("; ")]}
            )
            if isinstance(cookies, str)
            else cookies
        )
        self.page: int = 1
        self.end_page: int | None = None

    @staticmethod
    def get_company_name_from_html(html: str) -> str:
        soup = BeautifulSoup(html, "html.parser")
        return "".join(tag.get_text() for tag in soup)

    def get_companies_by_page(self) -> t.Generator[dict, None, None]:
        while True:
            try:
                yield from self.get_companies()
                self.page += 1
                if self.end_page is not None and self.page > self.end_page:
                    break

            except Exception as e:
                print(f"查询异常: {e}")
                raise

            finally:
                time.sleep(0.5)

    def get_companies(self) -> t.Generator[dict, None, None]:
        json_data = {
            "keywords": self.company,
            "sortField": None,
            "sortType": None,
            "pageIndex": self.page,
            "pageSize": 20,
            "condition": "{}",
        }

        api = "https://taoti-api.tankecha.com/data/api/v1/company/list/filter"

        print(f"正在查询{self.company}的搜索结果第{self.page}页...")

        resp = requests.post(
            api,
            headers=headers,
            cookies=self.cookies,
            json=json_data,
        )

        resp.raise_for_status()
        try:
            resp = Response(**resp.json())
        except Exception as e:
            print(f"解析异常: {e}")
            raise
        resp.raise_for_status()

        total_companies, records = resp.data["totalRecords"], resp.data["records"]  # type: ignore

        if self.end_page is None:
            self.end_page = calculate_page(total_companies)
            print(f"公司{self.company}有{total_companies}条记录, 共{self.end_page}页")

        return records

    def get_all_companies(self) -> t.Generator[tuple[str, str], None, None]:
        for company_info in self.get_companies_by_page():
            company_id = company_info["id"]
            company_name = company_info["companyName"]
            company_fullname = self.get_company_name_from_html(company_name)
            juridical_person = company_info["juridicalPerson"]
            if company_fullname.strip() == self.company:
                print(f"查询到公司: {company_fullname} id: {company_id} 开始查询联系人")
                yield company_id, juridical_person


class FindContactsByCompanyId:
    # 类的构造函数，初始化时需要传入公司ID和cookies
    def __init__(self, company_id: str, cookies: str) -> None:
        self.company_id = company_id  # 保存公司ID
        self.cookies = (
            cookiejar_from_dict(
                {k: v for k, v in [cookie.split("=") for cookie in cookies.split("; ")]}
            )
            if isinstance(cookies, str)  # 如果cookies是字符串，将其转换为cookiejar对象
            else cookies  # 否则直接使用传入的cookies
        )
        self.page: int = 1  # 初始化当前页码为1
        self.end_page: int | None = None  # 初始化结束页码为None

    # 获取指定页码的联系人信息
    def get_contacts_by_page(self) -> t.Generator[dict, None, None]:
        while True:  # 使用无限循环来遍历所有页
            try:
                yield from self.get_contacts()  # 获取当前页的联系人信息并返回
                self.page += 1  # 页码加1，移动到下一页

                if self.page > 5:  # 如果当前页码超过5，停止获取更多页面
                    break
                
                if self.end_page is not None and self.page > self.end_page:
                    break  # 如果已达到最后一页，则跳出循环

            except Exception as e:
                print(f"查询异常: {e}")  # 打印异常信息
                raise  # 抛出异常

            finally:
                time.sleep(0.5)  # 每次请求后暂停0.5秒，防止请求过于频繁

    # 获取当前页的联系人信息
    def get_contacts(self):
        api = f"https://taoti-api.tankecha.com/data/api/v1/company/contactlist/{self.company_id}"  # API地址，使用公司ID
        json_data = {
            "pageIndex": self.page,  # 当前页码
            "pageSize": 20,  # 每页显示的记录数
            "type": "0",  # 请求类型参数
        }

        print(f"正在查询{self.company_id}的搜索结果第{self.page}页...")  # 打印日志信息

        resp = requests.post(
            api,
            headers=headers,  # 使用预定义的headers
            cookies=self.cookies,  # 使用cookies进行身份验证
            json=json_data,  # 请求的数据
        )

        resp.raise_for_status()  # 检查HTTP响应状态

        try:
            resp = Response(**resp.json())  # 将响应的JSON数据转换为Response对象
        except Exception as e:
            print(f"解析异常: {e} 返回体: {resp.json()}")  # 打印解析异常信息
            raise  # 抛出异常

        resp.raise_for_status()  # 再次检查响应状态

        total_contacts, records = resp.data["totalRecords"], resp.data["records"]  # 获取总联系人数和当前页的记录

        if self.end_page is None:
            self.end_page = calculate_page(total_contacts)  # 计算总页数
            print(f"公司{self.company_id}有{total_contacts}个联系人记录, 共{self.end_page}页")  # 打印日志信息

        return records  # 返回当前页的记录

    def char_match(self, name1: str, name2: str) -> bool:
        """检查name1中的任何字符是否在name2中也存在"""
        return any(char in name2 for char in name1)
    
    # 获取所有联系人信息
    def get_all_contacts(self, juridical_person: str) -> t.Generator[Contact, None, None]:
        contacts = []
        for contact_info in self.get_contacts_by_page():  # 遍历所有页
            phone_number = contact_info["contactDetail"]  # 获取联系人电话
            name = contact_info["contactPerson"]  # 获取联系人姓名

            


            pattern = r'^\d{11}$'  # 定义电话号码的正则表达式
            if re.match(pattern, phone_number):
                # 如果找到了有效的电话号码但没有联系人名字，则将名字设置为“无”
                if not name:
                    name = "无"

                source_more = contact_info['comeFromArr']
                source = ", ".join([s['comeFromName'] for s in source_more]) if source_more else ""
                agent_account = contact_info['agentAccount']
                if agent_account:
                    continue

                # 创建Contact对象
                contact = Contact(
                    name=name,
                    phone_number=phone_number,
                    source=source,
                )
                contacts.append(contact)

        # 对联系人进行排序，如果联系人名字中的任意一个字符和法人字符匹配，排在前面
        contacts.sort(key=lambda contact: not self.char_match(contact.name, juridical_person))

        # 仅保留列表中的前5个联系人
        for contact in contacts[:5]:
            yield contact
            
##            print(f"查询到联系人: {name} 联系方式: {phone_number} 来源: {source}")  # 打印联系人信息
##            yield Contact(
##                name=name,
##                phone_number=phone_number,
##                source=source,  # 生成Contact对象并返回
##            )



class FindContacts:
    def __init__(self, excel_path: Path, cookies: str) -> None:
        self.excel_path = excel_path
        self.cookies = cookiejar_from_dict(
            {k: v for k, v in [cookie.split("=") for cookie in cookies.split("; ")]}
        )

    def get_company_list_from_xlsx(self) -> t.Generator[str, None, None]:
        wb = openpyxl.load_workbook(self.excel_path)
        sheet = wb["Sheet1"]
        header_row = [cell.value for cell in sheet[1]]
        company_column_index = header_row.index("供应商名称") + 1
        for row in sheet.iter_rows(
                min_row=2, min_col=company_column_index, max_col=company_column_index
        ):
            try:
                company_name = row[0].value
                if company_name is not None:
                    # 替换中文括号为英文括号
                    company_name = company_name.replace("（", "(").replace("）", ")")
                    yield company_name.strip()  # type: ignore
            except IndexError:
                continue

    def get_contacts(self) -> t.Generator[tuple[str, Contact, str], None, None]:
        for company in self.get_company_list_from_xlsx():
            if len(company) >= 20:
                continue
            print(f"正在查询公司: {company}")

            find_companies = FindCompanies(company, self.cookies)

##            for company_id, juridical_person in find_companies.get_all_companies():
##                find_contacts = FindContactsByCompanyId(company_id, self.cookies)
##                contacts = list(find_contacts.get_all_contacts(juridical_person))
##                # 直接使用返回的联系人列表
##                for contact in contacts:
##                    yield company, contact, juridical_person
            
            for company_id, juridical_person in find_companies.get_all_companies():
                find_contacts = FindContactsByCompanyId(company_id, self.cookies)
                contacts = list(find_contacts.get_all_contacts(juridical_person))

                if contacts:
                    # 如果找到了联系人，则生成每个联系人的信息
                    for contact in contacts:
                        yield company, contact, juridical_person
                else:
                    # 如果没有找到联系人，只生成公司和法人信息，联系人信息为空
                    yield company, None, juridical_person
                

    def run(self):
        wb = openpyxl.load_workbook(self.excel_path)
        try:
            contacts_sheet = wb["联系人信息"]
        except Exception:
            contacts_sheet = wb.create_sheet("联系人信息")

        container = defaultdict(list)

        header_rows = [
            "供应商名称",
            "法人",
        ]
        #for i in range(1, 11):
        #    header_rows.extend([f"联系人{i}", f"联系方式{i}", f"来源{i}"])

        contacts_sheet.append(
            header_rows
        )

        for company, contact, juridical_person in self.get_contacts():
            if contact is not None:
                # 如果contact不是None，正常处理
                container[company].append(
                    {
                        "company": company,
                        "juridical_person": juridical_person,
                        "name": contact.name,
                        "phone_number": contact.phone_number,
                        "source": contact.source,
                    }
                )
            else:
                # 如果contact是None，使用默认值或跳过
                container[company].append(
                    {
                        "company": company,
                        "juridical_person": juridical_person,
                        "name": "",  # 使用空字符串或其他默认值
                        "phone_number": "",
                        "source": "",
                    }
                )
        for company, contacts in container.items():
            juridical_person = contacts[0]["juridical_person"]
            row_value = [company, juridical_person]
            if len(contacts) > 10:
                contacts = contacts[:10]
            for contact in contacts:
                row_value.extend([contact["name"], contact["phone_number"], contact["source"]])
            contacts_sheet.append(row_value)

        wb.save(self.excel_path)


@click.command()
@click.option("--excel-path", "-e", type=Path, required=True, help="excel文件路径")
@click.option(
    "--cookies", "-c", type=Path, required=True, help="HTTP请求头: Cookie的文件路径"
)
@click.option(
    "--auth", "-a", type=Path, required=True, help="HTTP请求头: Authorization的文件路径"
)
def main(excel_path: Path, cookies: Path, auth: Path):
    headers["Authorization"] = auth.read_text().strip()
    f = FindContacts(excel_path, cookies.read_text().strip())
    f.run()


if __name__ == "__main__":
    main()
